<?php
/*
 * @Author: 李志刚
 * @CopyRight: 2022-2099 李志刚（854378082@qq.com）
 * @Date: 2022-02-10 08:25:58
 * @Description: 一些硬件对接的通用解析/数据转换方法
 * @LastEditors: 李志刚
 * @LastEditTime: 2022-04-01 14:40:51
 * @FilePath: /jichongchong-api/app/Customize/ComUnit.php
 */

declare(strict_types=1);

namespace App\Customize;

class ComUnit
{
    // 柏来 十进制转十六进制，前补0
    public static function bl_decto_bin($data, $length = 4)
    {
        $d = dechex($data);
        $d = str_pad($d, $length, '0', STR_PAD_LEFT);
        return strtoupper($d);
    }
    /* 
    `bkv = kv + kv + kv + ...`  
    `kv = length + [key length] + key + value`  
    `length = len(key length + key + value)`   
    `[key length] = len(key)`
    */
    // bkv解析，start 为从那个位置开始 ,length 为字符串减去的长度
    public static function bkvToArr($str, $start = 8, $length = 14)
    {
        try {
            $s_length = strlen($str) - $length;
            $i = $start;
            $arr = [];
            while ($i <= $s_length) {
                // 第一个KV长度
                $kv_length = (hexdec(substr($str, $i, 2)) + 1) * 2;
                $tmp = substr($str, $i, $kv_length);
                $key_length = substr($tmp, 2, 2) * 2;
                $key = substr($tmp, 4, $key_length);
                $arr[$key] = substr($tmp, $key_length + 4);
                $i += $kv_length;
            }
            return $arr;
        } catch (\Throwable $th) {
            dump($th->getLine() . ' - ' . $th->getMessage());
            return [];
        }
    }
    // 柏来 解析插孔数据
    public static function blDecodeKong($str)
    {
        // 0301 08 00 0301 09 80 0401 0a 0000 0401 0b 0000 0401 0c 000e 0401 0d 0000 0401 0e 0000 0401 55 08d2
        $arr = self::bkvToArr($str, 0, 0);
        // 单纯解析枪口状态
        $k = self::blJackStatus($arr['09']);
        $k['order_sn'] = hexdec($arr['0a']);
        $k['gonglv'] = hexdec($arr['0b']) * 0.1;
        $k['dianliu'] = hexdec($arr['0c']);
        $k['dianliang'] = hexdec($arr['0d']);
        $k['shijian'] = hexdec($arr['0e']);
        $k['dianya'] = hexdec($arr['55']) * 0.1;
        return $k;
    }
    /* 
    插孔状态
    bit0:过载位 0－正常，1过载
    bit1:过流位 0－正常，1过流
    bit2:过温  0－正常，1过温
    bit3:空载位 0－正常，1空载
    bit4－5:充电位 0－未充电，1充电，2充满自停，3时间到（金额用完）
    bit6:计量电路故障 0－正常，1－故障
    bit7:在线状态 0：离线 1：在线
    */
    public static function blJackStatus($str)
    {
        $bit_status = decbin(hexdec($str));
        $k['status_zaixian'] = substr($bit_status, 0, 1);
        $k['status_guzhang'] = substr($bit_status, 1, 1);
        $k['status_chongdian'] = bindec(substr($bit_status, 2, 2));
        $k['status_kongzai'] = substr($bit_status, 4, 1);
        $k['status_guowen'] = substr($bit_status, 5, 1);
        $k['status_guoliu'] = substr($bit_status, 6, 1);
        $k['status_guozai'] = substr($bit_status, 7, 1);
        return $k;
    }
    // 10进制转16进制，补足位数，高低反转
    public static function hcNumToBin($num, $length)
    {
        $tmp = str_pad(dechex($num), $length, '0', STR_PAD_LEFT);
        $result = implode('', array_reverse(str_split($tmp, 2)));
        return strtoupper($result);
    }
    // 10进制转16进制，补足位数，高低反转
    public static function hcBinToNum($string)
    {
        $tmp = str_split((string) $string, 2);
        $tmp = array_reverse($tmp);
        $result = hexdec(implode('', $tmp));
        return $result;
    }
    // 羽信物联bin转10进制
    public static function yxwlBinToNum($string)
    {
        $tmp = str_split((string) $string, 2);
        $tmp = array_reverse($tmp);
        $result = '';
        foreach ($tmp as $v) {
            $result .= bindec($v);
        }
        $result = ltrim($result, '0');
        return $result;
    }
    // CP56time2a 时间转北京时间
    public static function cp56ToDate($str)
    {
        $arr = array_reverse(str_split($str, 2));
        $y = hexdec($arr[0]);
        $m = hexdec($arr[1]);
        $d = hexdec($arr[2]);
        $h = hexdec($arr[3]);
        $i = hexdec($arr[4]);
        $s = hexdec($arr[5] . $arr[6]);
        $return = '20' . $y . '-' . $m . '-' . $d . ' ' . $h . ':' . $i . ':' . $s;
        return $return;
    }
    public static function dateToCp56()
    {
        $arr = str_split(date('YmdHis'), 2);
        $y = str_pad(dechex($arr[1]), 2, '0', STR_PAD_LEFT);
        $m = str_pad(dechex($arr[2]), 2, '0', STR_PAD_LEFT);
        $d = str_pad(dechex($arr[3]), 2, '0', STR_PAD_LEFT);
        $h = str_pad(dechex($arr[4]), 2, '0', STR_PAD_LEFT);
        $i = str_pad(dechex($arr[5]), 2, '0', STR_PAD_LEFT);
        $s = str_pad(dechex($arr[6]), 4, '0', STR_PAD_LEFT);
        $return = $s . $i . $h . $d . $m . $y;
        return strtoupper($return);
    }
    /* 
    假设16进制数3FH，转化为10进制数为63，如果直接问63的BCD码是多少，（BCD码是用4个位表示0~9）做法如下6的BCD码为0110，3的BCD码为0011，两个一组合为01100011转化为10进制为99，(就是将十进制每个位的数用BCD码表示),6*16的原因是为了将6往前移动4位。再举个例子16进制数ADH,其转化为十进制为173,其BCD码1*16*16+7*16+3=371，程序话应该很好实现，一个while循环就能搞定
     */
    // 解码BCD 16进制数据为10进制
    public static function bcdHexToDec($num)
    {
        $num = (string) hexdec($num);
        $str = '';
        for ($i = 0; $i < strlen($num); $i++) {
            $s = str_pad(decbin($num[$i]), 4, '0', STR_PAD_LEFT);
            $str .= $s;
        }
        return bindec($str);
    }
    // 解码压缩BCD 16进制数据为10进制，要逐位解析
    /* 
    对应二进制数为 0101 0110
    对应Hex数据为 0x56
    压缩BCD 码：一个字节可存放一个两位十进制数，其中高四位存放十位数，低四位存放个位数
    */
    public static function cbcdDecToHex($num)
    {
        $tarr = str_split((string) $num, 1);
        $str = '';
        foreach ($tarr as $t) {
            $s = str_pad(decbin($t), 4, '0', STR_PAD_LEFT);
            $str .= dechex(bindec($s));
        }
        return strtoupper($str);
    }
    /* 
    BIN码就是二进制编码。比如十进制数的11,用BIN码表示就是1011；左边的1表示十制的8,左边第三位的1表示十制的2,左边第四位的1表示十制的1, (即 8+2+1=11)
     */
    // 十进制数字转十六进制BIN码
    public static function binDecToHex($num)
    {
        // 转二进制
        $num = decbin($num);
        // 补足4位
        $num = str_pad($num, strlen($num) + 4 - (strlen($num) % 4), '0', STR_PAD_LEFT);
        $str = '';
        $j = str_split($num, 4);
        foreach ($j as $i) {
            $s = bindec($i);
            $s = dechex($s);
            $str .= $s;
        }
        return strtoupper($str);
    }
    // 字符串转ascii，ascii用16进制，防止出现三位数字的情况，长度不够右边补0
    public static function strToAsc($str, $length = 0, $pad = STR_PAD_RIGHT)
    {
        $dexArr = str_split((string) $str, 1);
        $str = '';
        foreach ($dexArr as $v) {
            // 10进制转16进制
            $t = base_convert(ord($v), 10, 16);
            $str .= $t;
        }
        // 补0
        if ($length > 0) $str = str_pad($str, $length, '0', $pad);
        return strtoupper($str);
    }
    // 16进制 ascii 码表转字符串，过滤掉尾部补位用的 00
    public static function ascToStr($asc)
    {
        $dexArr = str_split($asc, 2);
        $str = '';
        foreach ($dexArr as $v) {
            // 转成10进制以后去对应ascii
            $tmp = base_convert($v, 16, 10);
            if ($tmp == '00') {
                break;
            }
            $t = chr((int) $tmp);
            $str .= $t;
        }
        return $str;
    }
    // 字符串长度，10进制转16进制，并补充为2位
    public static function strToLength($str, $length)
    {
        $leng = number_format(strlen($str) / 2, 0, '', '');
        $num = base_convert($leng, 10, 16);
        $num_leng = strlen($num);
        if ($num_leng < $length) {
            // 单数位在前边补0，后边补0，双数只在后边补0
            if ($num_leng % 2 != 0) {
                $num = '0' . $num;
                $num_leng++;
            }
            $num = str_pad($num, $length, '0', STR_PAD_RIGHT);
        }
        return strtoupper($num);
    }
    // 金额补足到4位，8个数字
    public static function getJe($price, $length = 8)
    {
        $strlen = strlen((string) $price);
        for ($i = $strlen; $i < $length; $i++) {
            $price = '0' . $price;
        }
        return $price;
    }
    // 十进制转十六进制，前后补0，高低位转化
    public static function decto_bin($data)
    {
        $d = base_convert($data, 10, 16);
        $d = str_pad($d, 2, '0', STR_PAD_LEFT);
        if (strlen($d) == '3') {
            $d = substr($d, 1, 2) . '0' . substr($d, 0, 1);
        }
        $d = str_pad($d, 4, '0', STR_PAD_RIGHT);
        return strtoupper($d);
    }
    // 字符串长度十六进制，前后补0，高低位转化
    public static function str_decto_bin($data)
    {
        $length = number_format((strlen($data) + 4) / 2, 0, '', '');
        $d = base_convert($length, 10, 16);
        $d = str_pad($d, 2, '0', STR_PAD_LEFT);
        if (strlen($d) == '3') {
            $d = substr($d, 1, 2) . '0' . substr($d, 0, 1);
        }
        $d = str_pad($d, 4, '0', STR_PAD_RIGHT);
        return strtoupper($d);
    }
    // 计算校验和
    public static function getJyh($m)
    {
        $dexArr = str_split($m, 2);
        foreach ($dexArr as $key => $value) {
            $dexArr[$key] = hexdec($value);
        }
        $ck2 = dechex(array_sum($dexArr));
        $check = substr($ck2, strlen($ck2) - 2, 2);
        if (strlen($check) == '1') {
            $check = '0' . $check;
        }
        return $check;
    }

    // 字符串长度十六进制，4位，补0
    public static function bl_str_decto_bin($data)
    {
        $length = number_format((strlen($data) + 6) / 2, 0, '', '');
        $d = base_convert($length, 10, 16);
        $d = str_pad($d, 4, '0', STR_PAD_LEFT);
        return strtoupper($d);
    }
    // CRC16 校验和计算方法
    public static function getCrc16($string)
    {
        $string = pack('H*', $string);
        $crc = 0xFFFF;
        for ($x = 0; $x < strlen($string); $x++) {
            $crc = $crc ^ ord($string[$x]);
            for ($y = 0; $y < 8; $y++) {
                if (($crc & 0x0001) == 0x0001) {
                    $crc = (($crc >> 1) ^ 0xA001);
                } else {
                    $crc = $crc >> 1;
                }
            }
        }
        // 十进制转换为十六进制
        $l = dechex($crc % 256); // 低八位
        $h = dechex(floor($crc / 256)); // 高8位
        $l = str_pad($l, 2, '0', STR_PAD_LEFT);
        $h = str_pad($h, 2, '0', STR_PAD_LEFT);
        return strtoupper($l . $h);
    }
}
